package owl2vowl.parser.vowl.property;

import java.util.Collection;
import java.util.Iterator;
import java.util.Set;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.semanticweb.owlapi.model.IRI;

import owl2vowl.model.data.VowlData;
import owl2vowl.model.entities.HasReference;
import owl2vowl.model.entities.nodes.classes.VowlClass;
import owl2vowl.model.entities.nodes.classes.VowlThing;
import owl2vowl.model.entities.nodes.datatypes.VowlLiteral;
import owl2vowl.model.entities.properties.AbstractProperty;
import owl2vowl.model.entities.properties.TypeOfProperty;
import owl2vowl.model.entities.properties.VowlDatatypeProperty;
import owl2vowl.model.entities.properties.VowlObjectProperty;
import owl2vowl.model.visitor.VowlPropertyVisitor;

public class DomainRangeFiller implements VowlPropertyVisitor {
	private static final Logger logger = LogManager.getLogger(DomainRangeFiller.class);
	private final VowlData vowlData;
	private final Collection<? extends AbstractProperty> values;

	public DomainRangeFiller(VowlData vowlData, Collection<? extends AbstractProperty> values) {
		this.vowlData = vowlData;
		this.values = values;
	}

	public void execute() {
		this.fillEmpty();
		this.mergeMulti();
	}

	private void fillEmpty() {
		this.values.forEach((element) -> {
			if (!(element instanceof HasReference)) {
				try {
					element.accept(this);
				} catch (Exception arg2) {
					logger.info("          DomainRange Filler faild to accept element");
					logger.info("          Element: " + element);
					logger.info("          Reason: " + arg2);
					logger.info("          SKIPPING THIS ELEMENT *****");
				}

			}
		});
	}

	private VowlThing searchForConnectedThing(Set<IRI> value) {
		return value.size() != 1
				? null
				: this.vowlData.getThingProvider().getConnectedThing((IRI) value.iterator().next());
	}

	private void mergeMulti() {
		Iterator arg0 = this.values.iterator();

		while (arg0.hasNext()) {
			AbstractProperty value = (AbstractProperty) arg0.next();
			VowlClass domainUnion;
			if (value.getRanges().size() > 1) {
				domainUnion = this.vowlData.getGenerator().generateUnion(value.getRanges());
				value.setMergedRange(domainUnion.getIri());
			}

			if (value.getDomains().size() > 1) {
				domainUnion = this.vowlData.getGenerator().generateUnion(value.getDomains());
				value.setMergedDomain(domainUnion.getIri());
			}
		}

	}

	public void visit(VowlObjectProperty vowlObjectProperty) {
		this.classBehaviour(vowlObjectProperty);
	}

	public void visit(VowlDatatypeProperty property) {
		VowlThing vowlLiteral;
		if (property.getDomains().isEmpty() && property.getRanges().isEmpty()) {
			if (!property.getReferencedIris().isEmpty()) {
				property.setExportToJson(false);
				return;
			}

			vowlLiteral = this.vowlData.getThingProvider().getDisconnectedThing();
			VowlLiteral vowlLiteral1 = this.vowlData.getGenerator().generateLiteral();
			vowlLiteral.addOutGoingProperty(property.getIri());
			vowlLiteral1.addInGoingProperty(property.getIri());
			property.addDomain(vowlLiteral.getIri());
			property.addRange(vowlLiteral1.getIri());
		} else if (property.getDomains().isEmpty()) {
			vowlLiteral = this.searchForConnectedThing(property.getRanges());
			if (vowlLiteral == null) {
				vowlLiteral = this.vowlData.getThingProvider().getDisconnectedThing();
			}

			vowlLiteral.addOutGoingProperty(property.getIri());
			property.addDomain(vowlLiteral.getIri());
		} else if (property.getRanges().isEmpty()) {
			VowlLiteral vowlLiteral2 = this.vowlData.getGenerator().generateLiteral();
			vowlLiteral2.addInGoingProperty(property.getIri());
			property.addRange(vowlLiteral2.getIri());
		}

	}

	public void visit(TypeOfProperty typeOfProperty) {
		this.classBehaviour(typeOfProperty);
	}

	private void classBehaviour(AbstractProperty property) {
		VowlThing connectedThing;
		if (property.getDomains().isEmpty() && property.getRanges().isEmpty()) {
			if (!property.getReferencedIris().isEmpty()) {
				property.setExportToJson(false);
				return;
			}

			connectedThing = this.vowlData.getThingProvider().getDisconnectedThing();
			connectedThing.addOutGoingProperty(property.getIri());
			connectedThing.addInGoingProperty(property.getIri());
			property.addDomain(connectedThing.getIri());
			property.addRange(connectedThing.getIri());
		} else if (property.getDomains().isEmpty()) {
			connectedThing = this.searchForConnectedThing(property.getRanges());
			if (connectedThing == null) {
				connectedThing = this.vowlData.getGenerator().generateThing();
			}

			connectedThing.addOutGoingProperty(property.getIri());
			property.addDomain(connectedThing.getIri());
		} else if (property.getRanges().isEmpty()) {
			connectedThing = this.searchForConnectedThing(property.getDomains());
			if (connectedThing == null) {
				connectedThing = this.vowlData.getGenerator().generateThing();
			}

			connectedThing.addInGoingProperty(property.getIri());
			property.addRange(connectedThing.getIri());
		}

	}
}